using Sirenix.OdinInspector;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Utils.UGUI
{
    [ExecuteAlways]
    public class UIRoundRectangle : UIBehaviour , IMaterialModifier
    {
        private static readonly int s_RectSize = Shader.PropertyToID("_RectSize");
        private static readonly int s_RadiusTL = Shader.PropertyToID("_RadiusTL");
        private static readonly int s_RadiusTR = Shader.PropertyToID("_RadiusTR");
        private static readonly int s_RadiusBL = Shader.PropertyToID("_RadiusBL");
        private static readonly int s_RadiusBR = Shader.PropertyToID("_RadiusBR");
        private static readonly int s_Outline = Shader.PropertyToID("_OutSide");
        [SerializeField][ReadOnly] Shader roundRectangleShader;
        [ShowInInspector][ReadOnly] Material mat;
        [SerializeField][OnValueChanged("OnRoundTopRadiusChanged")] private Vector2 roundTopRadius;
        [SerializeField][OnValueChanged("OnRoundBottomRadiusChanged")] private Vector2 roundBottomRadius;
        [SerializeField][Range(0,1f)] [OnValueChanged("OnRoundRadiusScaleChanged")]float roundRadiusScale = 1f;
        [SerializeField][OnValueChanged("OnOutSideChanged")] float outSide = 1f;
        [SerializeField] Graphic graphic;

        
        
        public Vector2 RoundTopRadius
        {
            get => roundTopRadius;
            set
            {
                roundTopRadius = value;
                OnRoundTopRadiusChanged();
            }
        }
        public Vector2 RoundBottomRadius
        {
            get => roundBottomRadius;
            set
            {
                roundBottomRadius = value;
                OnRoundBottomRadiusChanged();
            }
        }
        public float RoundRadiusScale
        {
            get => roundRadiusScale;
            set
            {
                roundRadiusScale = value;
                OnRoundRadiusScaleChanged();
            }
        }
        public float OutSide
        {
            get => outSide;
            set
            {
                outSide = value;
                OnOutSideChanged();
            }
        }

        private Material Material
        {
            get
            {
                if (mat == null)
                {
                    mat = new Material(roundRectangleShader);
                    graphic.material = mat;
                }
                return mat;
            }
        }

        public Material GetModifiedMaterial(Material baseMaterial)
        {
            if (roundRectangleShader == null || graphic == null)
                return baseMaterial;

            // 每次都基于 baseMaterial + roundRectangleShader 新建，保证 stencil 参数正确
            if (mat == null || mat.shader != roundRectangleShader)
            {
                // 用 roundRectangleShader 构建，同时带上 baseMaterial 的属性
                mat = new Material(baseMaterial)
                {
                    shader = roundRectangleShader
                };
            }
            else
            {
                // 只同步 stencil 参数，避免覆盖你自己设置的属性
                SyncStencilProperties(baseMaterial, mat);
            }

            // 设置自定义 shader 参数
            mat.SetFloat(s_Outline, outSide);
            mat.SetFloat(s_RadiusTL, roundTopRadius.x * roundRadiusScale);
            mat.SetFloat(s_RadiusTR, roundTopRadius.y * roundRadiusScale);
            mat.SetFloat(s_RadiusBL, roundBottomRadius.x * roundRadiusScale);
            mat.SetFloat(s_RadiusBR, roundBottomRadius.y * roundRadiusScale);
            mat.SetVector(s_RectSize, graphic.rectTransform.rect.size);

            return mat;
        }

        /// <summary>
        /// 只拷贝 stencil 相关的属性，避免全量覆盖
        /// </summary>
        private void SyncStencilProperties(Material src, Material dst)
        {
            if (src.HasProperty("_Stencil")) dst.SetInt("_Stencil", src.GetInt("_Stencil"));
            if (src.HasProperty("_StencilOp")) dst.SetInt("_StencilOp", src.GetInt("_StencilOp"));
            if (src.HasProperty("_StencilComp")) dst.SetInt("_StencilComp", src.GetInt("_StencilComp"));
            if (src.HasProperty("_StencilWriteMask")) dst.SetInt("_StencilWriteMask", src.GetInt("_StencilWriteMask"));
            if (src.HasProperty("_StencilReadMask")) dst.SetInt("_StencilReadMask", src.GetInt("_StencilReadMask"));
            if (src.HasProperty("_ColorMask")) dst.SetInt("_ColorMask", src.GetInt("_ColorMask"));
        }
        
#if UNITY_EDITOR
        protected override void OnValidate()
        {
            base.OnValidate();
            SetContent();
        }

        protected void OnDrawGizmosSelected()
        {
            SetContent();
        }
#endif
        
        protected override void OnCanvasHierarchyChanged()
        {
            base.OnCanvasHierarchyChanged();
            SetContent();
        }


        protected override void OnRectTransformDimensionsChange()
        {
            base.OnRectTransformDimensionsChange();
            SetRectSize();
        }
        
        private void OnRoundRadiusScaleChanged()
        {
            OnRoundTopRadiusChanged();
            OnRoundBottomRadiusChanged();
        }
        
        private void OnOutSideChanged()
        {
            if (graphic != null)
            {
                Material.SetFloat(s_Outline, outSide);
                graphic.SetMaterialDirty();
            }
        }
        
        private void OnRoundTopRadiusChanged()
        {
            if (graphic != null)
            {
                Material.SetFloat(s_RadiusTL, roundTopRadius.x * roundRadiusScale);
                Material.SetFloat(s_RadiusTR, roundTopRadius.y * roundRadiusScale);
                graphic.SetMaterialDirty();
            }
        }

        private void OnRoundBottomRadiusChanged()
        {
            if (graphic != null)
            {
                Material.SetFloat(s_RadiusBL, roundBottomRadius.x * roundRadiusScale);
                Material.SetFloat(s_RadiusBR, roundBottomRadius.y * roundRadiusScale);
                graphic.SetMaterialDirty();
            }
        }

        private void SetRectSize()
        {
            if (graphic != null)
            {
                Material.SetVector(s_RectSize, graphic.rectTransform.rect.size);
                graphic.SetMaterialDirty();
            }
        }

        [Button]
        private void SetContent()
        {
            Material.SetFloat(s_Outline, outSide);
            Material.SetFloat(s_RadiusTL, roundTopRadius.x * roundRadiusScale);
            Material.SetFloat(s_RadiusTR, roundTopRadius.y * roundRadiusScale);
            Material.SetFloat(s_RadiusBL, roundBottomRadius.x * roundRadiusScale);
            Material.SetFloat(s_RadiusBR, roundBottomRadius.y * roundRadiusScale);
            Material.SetVector(s_RectSize, graphic.rectTransform.rect.size);
            graphic.SetMaterialDirty();
        }
        
        #if UNITY_EDITOR
        protected override void Reset()
        {
            base.Reset();
            if (roundRectangleShader == null)
            {
                roundRectangleShader = Shader.Find("Custom/UI/RoundRectangle");
            }

            if (graphic == null)
            {
                graphic = GetComponent<Graphic>();
            }

            OnOutSideChanged();
            OnRoundTopRadiusChanged();
            OnRoundBottomRadiusChanged();
            SetRectSize();
        }
        #endif

        protected override void OnDestroy()
        {
            base.OnDestroy();
            if (mat != null)
            {
#if UNITY_EDITOR
                DestroyImmediate(mat);
#else
        Destroy(mat);
#endif
                mat = null;
            }
        }
    }
}
